下面我將介紹如何透過Shader製作一個光暈。
分三個階段:
我已經準備好範本程式碼。如果看到畫面是淡藍色,那代表這是正常顯示的範本。

https://codepen.io/umas-sunavan/pen/gOzEqwa?editors=1010
我們建立一個球體。球體的材質是由ShaderMaterial組成。而ShaderMaterial所使用的Shader程式碼,來自HTML。
+const addSphere = () => {
+	const vertex = document.getElementById('vertexShader').innerHTML
+	const fragment = document.getElementById('fragmentShader').innerHTML
+    const geo = new THREE.SphereGeometry(5,50,50)
+    const mat = new THREE.ShaderMaterial({
+		vertexShader: vertex,
+		fragmentShader: fragment,
+	})
+    const mesh = new THREE.Mesh(geo, mat)
+    scene.add(mesh)
+    return mesh
+}
+addSphere()
HTML:
+<div style="display: none">
+    <p id="fragmentShader">
+        #ifdef GL_ES
+        precision mediump float;
+        #endif
+        void main(void){
+					gl_FragColor=vec4(0.,0.,0.2,1.);
+        }
+    </p>
+    <p id="vertexShader">
+        void main(void){gl_Position=projectionMatrix*modelViewMatrix*vec4(position, 1.0);
+        }
+    </p>
+</div>

在vertex shader我們添加vertexNormal。vertexNormal 是我們即將從vertex shader傳入到fragment shader的變數,而normal則是three.js提供的變數。
+varying vec3 vertexNormal;
void main(void){
+  vertexNormal = normal;
	void main(void){gl_Position=projectionMatrix*modelViewMatrix*vec4(position, 1.0);
}
在fragment shader我們添加。
	#ifdef GL_ES
	precision mediump float;
	#endif
+	varying vec3 vertexNormal;
	void main(void){
		gl_FragColor=vec4(0.,0.,0.2,1.);
	}
過去我們有介紹過fragment shader,gl_FragColor會是每個像素最終的顏色。如果我們設置成vec4(0.2,0.2,0.4,1.),那大概長這樣:

我們加上「神秘的程式碼」:
	varying vec3 vertexNormal;
	void main(void){
+		float intensity = 1.05 - dot(vertexNormal, vec3(0.,0.,1.));
+		vec3 atmosphere = vec3(.3, .6, 1.) * intensity;
+		gl_FragColor=vec4(atmosphere,0.) + vec4(0.,0.,0.2,1.);
-		gl_FragColor=vec4(0.,0.,0.2,1.);
	}
如此一來,光暈就完成了。


https://codepen.io/umas-sunavan/pen/KKREJzq
你現在可能會充滿問號,對於為什麼可以形成光暈有滿滿的問號。
不過沒關係,我們接下來將用兩篇的篇幅解釋為什麼光暈可以生成。並且透過這個案例,不斷挖掘shader的原理,最後完整的釐清光暈的運作方式。
前兩篇,我們先講解原理,再討論實作方式。這樣的作法非常教科書。很多時候我們在Shader研究原理時,順序往往是倒過來的。意思是:我們常常會先看到一個很好的Shader程式碼,但不知道其原理,不斷推敲回去。
所以從本篇開始,我會從程式碼去推敲其作用,並且延伸其作用的概念,最後講解整個原理。